import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { InjectModel } from '@nestjs/mongoose';
import { Interval } from '@nestjs/schedule';
import { InjectRepository } from '@nestjs/typeorm';
import { Model } from 'mongoose';
import CMD from 'src/cmd';
import { ChargeStrategy } from 'src/entities/ChargeStrategy';
import { Device } from 'src/entities/Device';
import { LocalCacheService } from 'src/local-cache/local-cache.service';
import { MqttService } from 'src/mqtt/mqtt.service';
import { RedisService } from 'src/redis/redis.service';
import { ChargeCardOrderRealTimeInfo, ChargeCardOrderRealTimeInfoDocument } from 'src/schemas/ChargeCardOrder.schema';
import { ChargeOrderRealTimeInfo, ChargeOrderRealTimeInfoDocument } from 'src/schemas/ChargeOrder.schema';
import { DeviceDocument, DeviceRecord } from 'src/schemas/Device.schema';
import RealTimeInfo from 'src/schemas/RealTimeInfo';
import { In, Repository } from 'typeorm';


@Injectable()
export class DeviceService {
    private devicePortKey:string; //端口

    private deviceConnectQueue:string; //设备上下线队列
    private devicePortQueue:string; //端口队列
    

    private deivceUploadStatusQueue:string; //设备上报状态队列

    private replyDeviceChargeStrategyQueue:string; //回复设备请求充电策略

    private realTimeInfoQueue:string;

    private serverReplyDeviceTopic:string;

    constructor(
        private config:ConfigService,
        private localCacheService:LocalCacheService,
        private redis:RedisService,
        private mqtt:MqttService,
        @InjectRepository(Device) protected repository:Repository<Device>,
        @InjectRepository(ChargeStrategy) protected strategyRepository:Repository<ChargeStrategy>,

        @InjectModel(ChargeOrderRealTimeInfo.name) private chargeOrderRealTimeInfoModel: Model<ChargeOrderRealTimeInfoDocument>,
        @InjectModel(ChargeCardOrderRealTimeInfo.name) private chargeCardOrderRealTimeInfoModel: Model<ChargeCardOrderRealTimeInfoDocument>,
        @InjectModel(DeviceRecord.name) private deviceModel: Model<DeviceDocument>
    ) {
        this.deviceConnectQueue = this.config.get('local.deviceConnectQueue')
        this.devicePortKey = this.config.get('redisKey.devicePort') //端口
        this.devicePortQueue = this.config.get('local.devicePortQueue')

        this.deivceUploadStatusQueue =  this.config.get('local.deivceUploadStatusQueue')

        this.replyDeviceChargeStrategyQueue = this.config.get('local.replyDeviceChargeStrategyQueue')

        this.realTimeInfoQueue = this.config.get('local.realTimeInfoQueue')

        this.serverReplyDeviceTopic = this.config.get('mqtt.topic.serverReplyDevice') + '/' //服务器回复设备的
    }

    @Interval(3000)
    async connectQueue() {
        try {
            const time = Math.floor((new Date()).getTime() / 1000)
            const online = new Set<string>()
            const offline = new Set<string>()
            for(let i = 0; i < 200; i ++) {
                let data:any = await this.localCacheService.rPop(this.deviceConnectQueue)
                if(!data) break
                data = JSON.parse(data)
                const deviceId = data.deviceId
                if(data.status == 1) { //上线
                    offline.delete(deviceId)
                    online.add(deviceId)
                    
                }else {
                    online.delete(deviceId)
                    offline.add(deviceId)
                }
            }

            if(online.size > 0) {
                console.log(`注册${online.size}条`)
                const where = {simCardNo : In(Array.from(online))}
                this.repository.update(where, {connectStatus : 1, onlineTime : time}).then(res => {
                    console.log(res)
                })
                
                this.repository.find({
                    where,
                    select : ['portCount', 'merchantId', 'simCardNo', 'deviceId', 'strategyId']
                }).then(devices => {
                    console.log(`查询到设备${devices.length}台`)
                    const time = String(Math.floor((new Date()).getTime() / 1000))
                    devices.forEach(device => {
                        this.mqtt.publish(this.serverReplyDeviceTopic + device.simCardNo, {txnNo : time, cmdType : CMD.CMD_SER_AUTHORIZE, authorResult : 1, outletQty : device.portCount, devSn : String(device.deviceId), agentId:device.merchantId, qrUrl : ''})
                        this.localCacheService.store.hmset(device.simCardNo, {deviceId : device.deviceId, strategyId : device.strategyId})
                    })
                })
            }

            if(offline.size > 0) {
                this.repository.update({simCardNo : In(Array.from(offline))}, {connectStatus : 0, offlineTime : time})
            }
        } catch (error) {
            
        }
    }

    async device(simCardNo:string) {
        const device = await this.localCacheService.hgetall(simCardNo)
        if(device) {
            return device
        }else {
            const device = await this.repository.findOne({
                where : {simCardNo},
                select : ['portCount', 'merchantId', 'simCardNo', 'deviceId', 'strategyId']
            })

            if(device) {
                this.localCacheService.store.hmset(simCardNo, {deviceId : device.deviceId, strategyId : device.strategyId})
            }

            return device
        }
    }

    /***
     * 更新设备端口状态队列
     * 设备上报心跳时候带有每个端口的状态
     * **/
    @Interval(3000)
    async devicePort() {
        try {
            for(let i = 0; i < 500; i ++) {
                let data:any = await this.localCacheService.rPop(this.devicePortQueue)
                if(!data) break
                data = JSON.parse(data)
                const status = []
                data.status.forEach((s, i) => {
                    status.push(i + 1, s)
                })
                this.redis.store.hmset(this.devicePortKey + data.deviceId, ...status)
            }
        } catch (error) {
            
        }
        
    }

    /**
     * 更新设备或者端口信息
     * 设备状态发生变化时，会立刻上报状态数据给服务器，状态数据包括端口状态、高低温、高低压、烟雾报警、保险丝烧断、继电器黏连等状态信息。
     * 可能是设备的，可能是某个端口的，根据portNo来判断，如果portNo==0，就是设备的
     * **/
    //@Interval(5000)
    async deviceStatus() {
        try {
            for(let i = 0; i < 200; i ++) {
                let data:any = await this.localCacheService.rPop(this.deivceUploadStatusQueue)
                if(!data) break
                data = JSON.parse(data)
                if(data.data && data.data.length > 0) {
                    const info = data.data[0]
                    const update = data.portNo == 0 ? {data : info} : {port : {[data.portNo] : info}}
                    this.deviceModel.updateOne(
                        {_id : data.deviceId},
                        {$set : update},
                        {upsert : true},
                        (error, res) => {
                            console.log(error)
                            console.log(res)
                        }
                    )

                }
                
            }
        } catch (error) {
            
        }
    }

    /**
     * 响应设备请求充电策略
     * 设备在联网成功后，会主动请求一次后台系统运行参数
     * **/
    //@Interval(5000)
    async replyDeviceStrategy() {
        try {
            const simCardNosSet = new Set()
            for(let i = 0; i < 200; i ++) {
                let simCardNo:any = await this.localCacheService.rPop(this.replyDeviceChargeStrategyQueue)
                if(!simCardNo) break
                simCardNosSet.add(simCardNo)
            }
            if(simCardNosSet.size > 0) {
                const simCardNos = Array.from(simCardNosSet)

                //默认的策略
                let defaultStrategy:any = await this.localCacheService.get('defaultStrategy')
                if(defaultStrategy) {
                    defaultStrategy = JSON.parse(defaultStrategy)
                }else {
                    const strategy = await this.strategyRepository.findOne({
                        select : ['integrated']
                    })
                    defaultStrategy = strategy.integrated
                    this.localCacheService.store.set('defaultStrategy', JSON.stringify(defaultStrategy))
                }

                // const devices = await this.repository.find({
                //     where : {simCardNo : In(simCardNos)},
                //     select : ['strategyId', 'simCardNo']
                // })
                const devices:{simCardNo : string, strategyId:any}[] = []
                simCardNos.forEach(async(cardNo:string) => {
                    const strategyId = await this.localCacheService.hget(cardNo, 'strategyId')
                    devices.push({simCardNo : cardNo, strategyId})
                })

                
                const unStrategyDevice = [] //没有设置策略的设备
                const strategyIdsSet = new Set() //有设置策略的设备id
                devices.forEach(device => {
                    if(device.strategyId) {
                        strategyIdsSet.add(device.strategyId)
                        //simCardNosSet.delete(device.simCardNo)
                    }else {
                        unStrategyDevice.push(device.simCardNo)
                    }
                })

                defaultStrategy = {
                    cmdType : CMD.CMD_DEV_CHK_SYS_RUN_PARA,
                    packType : 1,
                    paramList : [{...defaultStrategy}]
                }

                //没有设置价格的
                unStrategyDevice.forEach(simCardNo => {
                    this.mqtt.publish(this.serverReplyDeviceTopic + simCardNo, defaultStrategy)
                })

                const strategyIds = Array.from(strategyIdsSet)
                if(strategyIds.length > 0) {
                    const strategysMap = new Map()

                    const strategys = await this.strategyRepository.find({
                        where : {strategyId : In(strategyIds)},
                        select : ['strategyId', 'integrated']
                    })
                    
                    strategys.forEach(strategy => {
                        strategysMap.set(strategy.strategyId, {
                            cmdType : CMD.CMD_DEV_CHK_SYS_RUN_PARA,
                            packType : 1,
                            paramList : [strategy.integrated]
                        })
                    })


                    devices.forEach(device => {
                        this.mqtt.publish(this.serverReplyDeviceTopic + device.simCardNo, strategysMap.has(device.strategyId) ? strategysMap.get(device.strategyId) : defaultStrategy)
                    })
                }
                
                
            }

        } catch (error) {
            
        }
    }

    async realTimeInfo() {
        try {
            const date = new Date()
            const time = Math.floor(date.getTime() / 1000)
            for(let i = 0; i < 1000; i ++) {
                let data:any = await this.localCacheService.rPop(this.realTimeInfoQueue)
                if(!data) break
                data = JSON.parse(data)
                const infoData:RealTimeInfo = {
                     updateTime : time,
                     consumeTime : data.chargeTime,
                     power : data.chargePwr / 100,
                     voltage : data.chargeVol / 100,
                     current : data.chargeCur / 1000,
                     consumeEnergy : data.chargeEng / 1000,
                     temperature : data.portTemp / 10
                }

                const update = [
                    {_id : data.orderMsgId},
                    {$set : {lastData : infoData}, $push : { data : infoData}},
                    {upsert : true},
                    (error, res) => {
                        console.log(error)
                        console.log(res)
                    }
                ]

                if(data.cardid) { //卡的
                    this.chargeCardOrderRealTimeInfoModel.updateOne(...update)
                }else {
                    this.chargeOrderRealTimeInfoModel.updateOne(...update)
                }
            }
        } catch (error) {
            
        }
    }
}
